package it.yuzuojian.com.util;

import it.yuzuojian.com.annotation.aop.*;
import it.yuzuojian.com.annotation.ioc.*;
import it.yuzuojian.com.annotation.mvc.Controller;
import it.yuzuojian.com.aop.*;
import it.yuzuojian.com.handle.mvc.DateHandle;
import it.yuzuojian.com.handleiInterface.BeanPostProcessor;
import it.yuzuojian.com.handleiInterface.InitializingBean;
import it.yuzuojian.com.pojo.BeanDefinition;
import it.yuzuojian.com.pojo.BeanFactory;
import it.yuzuojian.com.pojo.BeanPostProcessorOrder;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicationContextUtil {
    private BeanFactoryUtil beanFactoryUtil = new BeanFactoryUtil();
    private List<BeanPostProcessorOrder> beanPostProcessorOrders = BeanFactory.getBeanPostProcessorOrders();
    private List<DateHandle> dateHandles = BeanFactory.getDateHandles();
    private List<String> mvcStaticResources  = BeanFactory.getMvcStaticResources();

    /**
     * 初始化bean的后置处理器
     */
    public void initBeanPostProcessors() {
        ConcurrentHashMap<String, BeanDefinition> beanDefinitions = BeanFactory.getBeanDefinitions();
        beanDefinitions.forEach((beanName, beanDefinition) -> {
            if (beanDefinition.isBeanPostProcessor()) {
                try {
                    Class clazz = beanDefinition.getClazz();
                    if (clazz.isAnnotationPresent(Order.class)) {
                        BeanPostProcessorOrder beanPostProcessorOrder = new BeanPostProcessorOrder(((Order) clazz.getDeclaredAnnotation(Order.class)).value(), (BeanPostProcessor) getBean(beanName));
                        beanPostProcessorOrders.add(beanPostProcessorOrder);
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }

            }
        });
        Collections.sort(beanPostProcessorOrders, new Comparator<BeanPostProcessorOrder>() {
            public int compare(BeanPostProcessorOrder a1, BeanPostProcessorOrder a2) {
                return a1.getOrder() - a2.getOrder();
            }
        });

    }

    /**
     * 初始化mvc配置
     */
    public void  initWebMvcConfigurer() {
        ConcurrentHashMap<String, BeanDefinition> beanDefinitions = BeanFactory.getBeanDefinitions();
        beanDefinitions.forEach((beanName, beanDefinition) -> {
            if (beanDefinition.isWebMvcConfigurer()) {
                try {
                    Class clazz = beanDefinition.getClazz();
                    Method method = clazz.getDeclaredMethod("addStaticResourceURL");
                    List<String>  invoke = (List<String>)method.invoke(clazz.newInstance());
                    for (String s : invoke) {
                        mvcStaticResources.add(s);
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                }

            }
        });
    }
    /**
     * 初始化mvc的数据处理器
     */
    public void initMVCDataHandle() {
        ConcurrentHashMap<String, BeanDefinition> beanDefinitions = BeanFactory.getBeanDefinitions();
        beanDefinitions.forEach((beanName, beanDefinition) -> {
            if (beanDefinition.isDateHandle()) {
                try {
                    dateHandles.add((DateHandle) getBean(beanName));
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }

            }
        });
    }
    /**
     * 初始化aop容器
     */
    public void initAspect() {
        ConcurrentHashMap<String, BeanDefinition> beanDefinitions = BeanFactory.getBeanDefinitions();

        List<GPAopConfig> configs = new ArrayList<>();
        beanDefinitions.forEach((beanName, beanDefinition) -> {
            if (beanDefinition.isAspect()) {
                Class clazz = beanDefinition.getClazz();
                GPAopConfig aopConfig = new GPAopConfig();
                if (clazz.isAnnotationPresent(Order.class)) {
                    aopConfig.setOrder(((Order) clazz.getDeclaredAnnotation(Order.class)).value());
                } else {
                    aopConfig.setOrder(1);
                }
                Method[] declaredMethods = clazz.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {

                    String methodString = declaredMethod.getName();
                    if ("wait".equals(methodString) || "equals".equals(methodString) || "toString".equals(methodString) ||
                            "hashCode".equals(methodString) || "getClass".equals(methodString) || "notify".equals(methodString) ||
                            "notifyAll".equals(methodString)) {
                        continue;
                    }
                    aopConfig.setAspectClass(clazz.getName());
                    try {
                        if (declaredMethod.isAnnotationPresent(PointCut.class)) {
                            PointCut annotation = declaredMethod.getAnnotation(PointCut.class);
                            aopConfig.setPointCut_class(annotation.class_name());
                            aopConfig.setPointCut_package(annotation.package_name());
                            aopConfig.setPointCut_method(annotation.method_name());
                        }
                        if (declaredMethod.isAnnotationPresent(Before.class)) {
                            aopConfig.setAspectBefore(declaredMethod.getName());
                        }
                        if (declaredMethod.isAnnotationPresent(After.class)) {
                            aopConfig.setAspectAfter(declaredMethod.getName());
                        }
                        if (declaredMethod.isAnnotationPresent(AfterThrowing.class)) {
                            aopConfig.setAfterThrowing(declaredMethod.getName());
                        }
                        if (declaredMethod.isAnnotationPresent(AfterReturning.class)) {
                            aopConfig.setAfterReturning(declaredMethod.getName());
                        }
                        if (declaredMethod.isAnnotationPresent(Around.class)) {
                            aopConfig.setAround(declaredMethod.getName());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
                configs.add(aopConfig);
            }
        });
        AopFactory.setConfigs(configs);

    }

    /**
     * 解析对象成beanDefinition
     *
     * @param allClass
     */
    public void initBeanDefinitions(List<Class<?>> allClass) {
        allClass.forEach((item) -> {
            if (item.isAnnotationPresent(Configuration.class)) {
                beanFactoryUtil.setBeanDefinition(item);
                Method[] declaredMethods = item.getDeclaredMethods();
                for (Method declaredMethod : declaredMethods) {
                    if (declaredMethod.isAnnotationPresent(Bean.class)) {
                        Parameter[] parameters = declaredMethod.getParameters();
                        Bean annotation = declaredMethod.getDeclaredAnnotation(Bean.class);
                        Class returnType = declaredMethod.getReturnType();
                        if (returnType.getName().equals("void")) {

                        } else {
                            Object invoke = null;
                            try {
                                invoke = declaredMethod.invoke(item.newInstance(), parameters);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            } catch (InstantiationException e) {
                                e.printStackTrace();
                            }
                            if (annotation.value().equals("")) {
                                beanFactoryUtil.setSingletonObject(declaredMethod.getName(), invoke);
                            } else {
                                beanFactoryUtil.setSingletonObject(annotation.value(), invoke);
                            }
                        }

                    }
                }
            }
            if (item.isAnnotationPresent(Component.class)) {
                Component declaredAnnotation = item.getDeclaredAnnotation(Component.class);
                if (declaredAnnotation.value().equals("")) {
                    beanFactoryUtil.setBeanDefinition(item);
                } else {
                    beanFactoryUtil.setBeanDefinition(item, declaredAnnotation.value());
                }
            }
            if (item.isAnnotationPresent(Controller.class)) {
                Controller declaredAnnotation = item.getDeclaredAnnotation(Controller.class);
                if (declaredAnnotation.value().equals("")) {
                    beanFactoryUtil.setBeanDefinition(item);
                } else {
                    beanFactoryUtil.setBeanDefinition(item, declaredAnnotation.value());
                }
            }
        });
    }


    public Object getBean(BeanDefinition beanDefinition,
                          ConcurrentHashMap<String, Object> originalObjects,
                          ConcurrentHashMap<String, Object> singletonObjects) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Object singleton = getSingleton(beanDefinition, originalObjects, singletonObjects);
        if (beanDefinition.getScope().equals("singleton")) {
            if (singleton != null) {
                return singleton;
            }
        }
        Class clazz = beanDefinition.getClazz();
        Object newInstance = clazz.newInstance();
        //aop
        GPAdviceSupport config = new GPAdviceSupport();
        config.setTargetClass(clazz);
        config.setTarget(newInstance);
        if (config.pointCutMatch()) {
            try {
                newInstance = new CglibDynamicAopProxy(config).getProxy();
            } catch (Exception e) {
                newInstance = new GPJDKDynamicAopProxy(config).getProxy();
            }

        }
        Object finalNewInstance1 = newInstance;
        beanFactoryUtil.setOriginalObject(beanDefinition.getBeanName(), finalNewInstance1);
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            autowiredHandle(newInstance, declaredField, originalObjects, singletonObjects);
        }

        beanPostProcessorOrders.forEach((beanPostProcessorOrder -> {//bean前置处理器--初始化之前
            beanPostProcessorOrder.getBeanPostProcessor().postProcessBeforeInitialization(finalNewInstance1, beanDefinition.getBeanName());
        }));
        if (finalNewInstance1 instanceof InitializingBean) {//初始化
            InitializingBean initializingBean = (InitializingBean) finalNewInstance1;
            initializingBean.afterPropertiesSet();
        }
        beanPostProcessorOrders.forEach((beanPostProcessorOrder -> {//bean后置处理器--初始化之后
            beanPostProcessorOrder.getBeanPostProcessor().postProcessAfterInitialization(finalNewInstance1, beanDefinition.getBeanName());
        }));
        if (beanDefinition.getScope().equals("singleton")) {
            beanFactoryUtil.setSingletonObject(beanDefinition.getBeanName(), finalNewInstance1);
        }
        return finalNewInstance1;
    }


    public void autowiredHandle(Object newInstance, Field declaredField,
                                ConcurrentHashMap<String, Object> originalObjects,
                                ConcurrentHashMap<String, Object> singletonObjects
    ) throws IllegalAccessException, InstantiationException, ClassNotFoundException {

        if (declaredField.getModifiers() == 2 || declaredField.getModifiers() == 10
                || declaredField.getModifiers() == 26 || declaredField.getModifiers() == 18) {
            declaredField.setAccessible(true);
        }
        if (declaredField.isAnnotationPresent(Autowired.class)) {
            ArrayList<Object> objects = new ArrayList<>();
            singletonObjects.forEach((key, value) -> {
                if (declaredField.getType().isAssignableFrom(value.getClass())) {
                    objects.add(value);
                }
            });
            if (objects.size() == 0) {
                List<BeanDefinition> beanDefinitions1 = beanFactoryUtil.getBeanDefinition(declaredField.getType());
                if (beanDefinitions1.size() == 0) {

                } else {
                    if (beanDefinitions1.size() == 1) {
                        Object bean = getBean(beanDefinitions1.get(0), originalObjects, singletonObjects);
                        declaredField.set(newInstance, bean);
                    } else {
                        for (BeanDefinition definition : beanDefinitions1) {
                            if (declaredField.getName().equals(definition.getBeanName())) {
                                Object bean = getBean(definition, originalObjects, singletonObjects);
                                declaredField.set(newInstance, bean);
                            }
                        }
                    }
                }
            } else {
                if (objects.size() == 1) {
                    try {
                        declaredField.set(newInstance, objects.get(0));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                } else {
                    singletonObjects.forEach((key, value) -> {
                        if (key.equals(declaredField.getName())) {
                            try {
                                declaredField.set(newInstance, value);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }

                    });
                }
            }

        }

    }

    public Object getBean(String beanName) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        ConcurrentHashMap<String, BeanDefinition> beanDefinitions = BeanFactory.getBeanDefinitions();
        ConcurrentHashMap<String, Object> originalObjects = BeanFactory.getOriginalObjects();
        ConcurrentHashMap<String, Object> singletonObjects = BeanFactory.getSingletonObjects();
        BeanDefinition beanDefinition = beanDefinitions.get(beanName);
        return getBean(beanDefinition, originalObjects, singletonObjects);
    }

    private Object getSingleton(BeanDefinition beanDefinition,
                                ConcurrentHashMap<String, Object> originalObjects,
                                ConcurrentHashMap<String, Object> singletonObjects) {
        String beanName = beanDefinition.getBeanName();
        // 先去一级缓存里拿,如果一级缓存没有拿到,去二级缓存里拿
        if (singletonObjects.containsKey(beanName)) {
            return singletonObjects.get(beanName);
        } else if (originalObjects.containsKey(beanName)) {
            return originalObjects.get(beanName);
        } else {
            return null;
        }
    }

}
